
#include <pic18.h> 
#include "main.h" 
/* #include "FUpdate.h"  */
/* #include "string.h"  */



/* #define   FALSE	0 */
/* #define   TRUE		1 */

//#define   E2_ADDR_PROGRAM_POINTER			0


/* Function prototypes */
void checksum ( void );
unsigned char gx( void );
unsigned char g2x( void );
void zap( void );
void WriteTable( void );
void WriteFlash( void );
void FlashErase ( void );

unsigned char ERR_NUM = 0;

//const unsigned char THE_PROGRAM_PLACE = 0;

#define   RTS			RC5			  /* Output. If RTS is set(high level) it's say to modem - "NOT to send data" */


/* how many bytes to write */
//#define 	MASK				0x07 	//8 bytes
#define	MASK				0x0F	//16 bytes
#define  BUFFER_SIZE		(MASK+1)		

unsigned char buff[BUFFER_SIZE];	/* 8 or 16 byte buffer used for writing to program memory */
unsigned char rec_buff[BUFFER_SIZE*2+2];	
unsigned char DO_NOT_INCREMENT;
unsigned char cksum = 0,rectype,bcount;
unsigned char index;
unsigned char rec_index;
unsigned long ProgramPointer;
unsigned long erase;
unsigned char Check_Sum;
static unsigned char temp;
static unsigned long Timeout = 0;

static persistent near unsigned char TBU_Memo = 0;

//unsigned int StringCnt;
unsigned int GlobalBytesCounter;	/* Packet length counter */

#define   TIMEOUT		0x2000000

/* void Switch_Boot_Pointer( void ) */
/* { */
/* 	if ( ProgramPointer == PROGRAM_START__SECOND_HALF ) */
/* 	{ */
/* 		e2_Write(E2_ADDR_PROGRAM_POINTER, SECOND_HALF); */
/* 	} */
/* 	else */
/* 	{ */
/* 		e2_Write(E2_ADDR_PROGRAM_POINTER, FIRST_HALF); */
/* 	} */
/* 	delay_ms(50); */
/* } */

void checksum ( void )
{
	g2x();
	if ( cksum )
		Check_Sum = 3;
	else
		Check_Sum = 0; 
}

//Get a 4 bit HEX number from the serial port
unsigned char gx(void)
{
	Timeout = TIMEOUT;
	while(!RC1IF) 
	{	
		if ( --Timeout == 0 )
			return FALSE; 
			//Execute_Module_1 = Command_Processor;
	}	
	
	EEDATA = RCREG1;

	if ( GlobalBytesCounter == 0 || --GlobalBytesCounter == 0 )
	{
		cksum = 4;				  /* ERROR */
	}

	if(EEDATA>='A')
		return ((EEDATA - (unsigned char)'A')+10);
	return (EEDATA - '0');
}
// :0AFFF00004EF80F080C0000005005FI
/* recieve a hexadecimal byte, add it to the checksum */
unsigned char g2x(void)
{
	unsigned char temp;
	temp = (gx()<<4);
	temp += gx();
	cksum += temp;
	return temp;
}

/* Initiate a write to memory */

void zap(void)
{
	WREN=1;
	EECON2=0x55;
	EECON2=0xAA;
	WR=1;
	NOP();
	while(WR);
	WREN=0;
}

/* transfer a byte from RAM to internal flash register(s) */

void WriteTable(void)
{
	if(DO_NOT_INCREMENT)	// address is already loaded in TBLPTR, no pre-increment needed
	{
		DO_NOT_INCREMENT=0;
		asm("tblwt*");
	}
	else
		asm("tblwt+*");	// otherwise TBLPTR must be pre-incremented
}

/* write the bytes buffer to flash */

void WriteFlash(void)
{
	if(DO_NOT_INCREMENT)
		TBLPTRL &= ~MASK;	// point to start of 8 byte panel
	for( index = 0; index < BUFFER_SIZE; index++ )
	{
		TABLAT = buff[index];
		WriteTable();
	}
	zap();
}

/* void ClearBuffer( void ) */
/* {	 */
/* 	for ( index = 0; index < BUFFER_SIZE; index++ ) */
/* 	{ */
/* 		buff[index] = 0xFF; */
/* 	} */
/* } */

void FlashErase ( void )
{
	erase = ProgramPointer;
	//erase = 0xFF00;
	
 	while( 1 )
	{
		TBLPTRL = (unsigned char)erase;
		TBLPTRH = (unsigned char)(erase>>8);
		TBLPTRU = (unsigned char)(erase>>16);
		EECON1 = 0x90;	// set up for flash erasure
		zap();
		
		erase += 64;

		if(erase >= (ProgramPointer + PROGRAM_SIZE - 64 ) )
			break;

	}
 
	TBLPTRU=0;
}

/* void SelectProgramPlace ( void ) */
/* { */
/* 	if ( EEPROM_READ(E2_ADDR_PROGRAM_POINTER) == SECOND_HALF ) */
/* 	{ */
/* 		ProgramPointer = PROGRAM_START__SECOND_HALF; */
/* 		INTERRUPT_VECTOR_SELECT = SECOND_HALF; */
/* 	} */
/* 	else */
/* 	{ */
/* 		ProgramPointer = PROGRAM_START__FIRST_HALF; */
/* 		INTERRUPT_VECTOR_SELECT = FIRST_HALF; */
/* 	} */
/* } */


/* return TRUE if Update successful */
unsigned char FirmwareUpdate ( void )
{
	//INTERRUPT_VECTOR_SELECT_DIR = 0; // as output
	
	far void* p = FirmwareUpdate; 	
	unsigned long CurrentProgramPlace = (unsigned long)p;
	
//	temp = RCREG1;					  /* clean RCREG */
	
	if ( CurrentProgramPlace > PROGRAM_START__SECOND_HALF ) /* Get pointer to update */
	{
		ProgramPointer = PROGRAM_START__FIRST_HALF; 
		FlashErase();
		DataMode_Transmitter("NX00", 0);

/* 		lcd_clear_str(3,2); */
/* 		lcd_putsf("NX00"); */
	}
	else
	{
		ProgramPointer = PROGRAM_START__SECOND_HALF;
		FlashErase();
		DataMode_Transmitter("NX01", 0);

/* 		lcd_clear_str(3,2); */
/* 		lcd_putsf("NX01"); */
	}

	TBU_Memo = 0;
	
  BEGIN_RECEIVE_HEADER:

	Check_Sum = 0;
	GlobalBytesCounter = 0;
	temp = 0;

	RTS = 0;							  /* Receive enable //flow control */

	for(;;)							  /* Parsing of Packet Header - get Bytes Count of packet */
	{
		Timeout = TIMEOUT;
		while(!RC1IF) 
		{	
			if ( --Timeout == 0 )
				return FALSE;
		}	

		if ( GlobalBytesCounter == 0 )
		{
			GlobalBytesCounter = RCREG1;
		}
		else
		{
			GlobalBytesCounter |= (unsigned int)RCREG1 << 8;
			break;
		}
	}

	Timeout = TIMEOUT;
	while(!RC1IF) 
	{	
		if ( --Timeout == 0 )
			return FALSE;
	}	

	temp = RCREG1;					  /* reserve byte */

	if ( GlobalBytesCounter > 3 )
		GlobalBytesCounter--;
	else
	{
		ERR_NUM = 1;	
		goto REC_ERROR;
	}

/* 		lcd_clear_str(3,2); */
/* 		lcd_putsf("RESERVE"); */
		//delay_ms(2000);

	//begin_RECEIVE_DATA:
	
//receive a hex file via the serial port and write it to program memory
	for(;;)		// loop until end of file1
	{		
		if ( GlobalBytesCounter == 0 )
		{
			ERR_NUM = 2;
			goto REC_ERROR;
		}

		RTS = 0;

		while ( Check_Sum )
		{								  /* if Check_Sum ERROR, then wait for end of the packet and receive his again*/
			Timeout = TIMEOUT;
			while(!RC1IF) 
			{	
				if ( --Timeout == 0 )
					return FALSE;
			}	

			temp = RCREG1;					  /* clean RCREG */

			lcd_clear_str(1,3);
			lcd_putsf("CS = ");
			lcd_print_Number(Check_Sum, LEFT);
			lcd_putsf(" TBLPTRU = ");
			lcd_print_Number(TBLPTRU, LEFT);
			
			if ( --GlobalBytesCounter == 0 )
			{
				ERR_NUM = 3;
				goto REC_ERROR;
			}
		}
		
		lcd_clear_str(0,3);

		Timeout = TIMEOUT;
		while(!RC1IF)   /* string begin  */
		{	
			if ( --Timeout == 0 )
				return FALSE;
		}	


		if ( --GlobalBytesCounter == 0 )
		{
			temp = RCREG1;
			DataMode_Transmitter(STR__NX, 0);
			goto BEGIN_RECEIVE_HEADER;
		}
		else
		{
			if ( RCREG1 != ':' )
			{
				ERR_NUM = 4;
				goto REC_ERROR;
			}
		}

		lcd_clear_str(0,2);
		lcd_putsf("  Count: ");
		lcd_print_Number(GlobalBytesCounter, LEFT);

		
//		:10 03 A8 00 6A18EB1796179F17F916CB17D016DE1693

//    :02 00 00 04  00 00 FA

		cksum = bcount = g2x();		// get the byte count
		TBLPTRH = g2x();
		TBLPTRL = EEADR = g2x();
		DO_NOT_INCREMENT = 1;
		rectype = g2x();	// get the record type
		
		switch(rectype)
		{
			case DATA:	// data record
				// to protect bootloader from being overwritten
				TBLPTRU = TBU_Memo;
				if( (FLASH) && ( TBLPTRU == 0 ) && (TBLPTRH < (unsigned char)(PROGRAM_START__FIRST_HALF>>8)) )
				{
					Check_Sum = 1;
					break;
				}
				//ClearBuffer();
				MemSet(buff, BUFFER_SIZE, 0xFF );
				
				/* Get all string */
				temp = bcount + 1;
				rec_index = 0;
				while(temp--)
				{
					rec_buff[rec_index++] = g2x();
				}
				rec_index = 0;
				/*  */
				RTS = 1;
				
				if ( cksum )
				{
					Check_Sum = 2;
					break;
				}
			
								
				while(bcount--)
				{
					//TABLAT = EEDATA = buff[ (EEADR & MASK) ] = g2x();	// get the data
					TABLAT = EEDATA = buff[ (EEADR & MASK) ] = rec_buff[rec_index++];
					if((CONFIG)||(EEPROM))
					{
						if(CONFIG)	// EEPROM/config. bytes are written one byte at a time
							WriteTable();
						zap();
					}
					else
						if((EEADR & MASK) == MASK )	// program/IDLOCs are flashed 8 bytes at a time
						{
							WriteFlash();
							//ClearBuffer();
							MemSet(buff, BUFFER_SIZE, 0xFF );
						}
					EEADR++;
				}
				if( ((EEADR & MASK) !=0 ) && ( FLASH ) )
				{
            	WriteFlash();
				}
				//checksum();
				break;

			case END:	// end of hex file
				checksum();
				if ( Check_Sum )
					break;

				Timeout = TIMEOUT;
				while(!RC1IF)   /* wait for last byte - checksum  */
				{		
					if ( --Timeout == 0 )
						return FALSE;
				}	
				temp = RCREG1; 	  /* clear RCREG1 */
				
//				SelectProgramPlace();
				return TRUE;
				//(*((void(*)(void))PROGRAM_START__FIRST_HALF))(); // jump to new program
//				(*((void(*)(void))ProgramPointer))(); // jump to new program
//				break;

			case EXTEND_ADDRESS:	// extended address record
				while(bcount--)
				{
					EEADR = g2x();	// this byte determines whether EE, Config or ID data
				}
				
				EEPGD=1;
				if( EEADR == 0xF0 )
					EEPGD=0;	// select for EEPROM
					
				CFGS=0;
				if( (EEADR&0xF0) == 0x30 )
					CFGS=1;		// select for config write
					
				TBLPTRU = EEADR;
				TBU_Memo = TBLPTRU;
//				TBLPTRU = TBU_Memo;

				lcd_clear_str(0,2);
				lcd_putsf("TBU = ");
				lcd_print_Number(TBU_Memo, LEFT);
				
				lcd_putsf(" EEADR = ");
				lcd_print_Number(EEADR, LEFT);

				checksum();

				break;
		}
	
	}
	
// ATD0523974963
// 04 00 00 53 55 06
	
  REC_ERROR:

	lcd_clear_str(0,2);
	lcd_putsf(" RC_ERROR ");
	lcd_print_Number(ERR_NUM, LEFT);
	//delay_ms(3000);
	DataMode_Transmitter("ER00", 0);
	goto BEGIN_RECEIVE_HEADER;

//	return 0;
}

